// Copyright (C) 2008 Taylor L. Riche <riche@cs.utexas.edu>
//  
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software 
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

#include "System.h"

namespace lagniappe
{

System::System() 
{
}

System::~System()
{
}

ProcElement * 
System::getFreeProc(Operator * o, bool initial)
{
	// Checks for a free processing element, if there is none, returns null
	// The calling function then must decide what to do according to some 
	// policy. This does not mean the system is overloaded, it just means that
	// there are no processing elements that are completely empty.
	ProcElement * tempProc = NULL;
	assert(!procMap.empty());
	//if(!freeProcList.empty()) {
	//	tempProc = freeProcList.front();
	//	assert(tempProc != NULL);
	//	freeProcList.pop_front();
	//}
	//std::cerr << ((initial)?("True"):("False")) << std::endl;
	if(initial) {
		for(std::list<MemGroup *>::iterator iter = memGroupList.begin(); iter != memGroupList.end(); ++iter) {
			MemGroup * tempGroup = *iter;
			if(tempGroup->hasFree(initial)) {
				opToMemGroupMap[o].push_back(tempGroup);
				tempProc = tempGroup->getFreeProc(o);
				break;
			}
		}
		if(tempProc == NULL) {
			for(std::list<MemGroup *>::iterator iter = memGroupList.begin(); iter != memGroupList.end(); ++iter) {
				MemGroup * tempGroup = *iter;
				if(tempGroup->hasFree()) {
					opToMemGroupMap[o].push_back(tempGroup);
					tempProc = tempGroup->getFreeProc(o);
					break;
				}
			}
		}
	}
	else {
		std::list<MemGroup *> & tempList = opToMemGroupMap[o];
		for(std::list<MemGroup *>::iterator iter = tempList.begin(); iter != tempList.end(); ++iter) {
			if((*iter)->hasFree()) {
				tempProc = (*iter)->getFreeProc(o);
				std::cerr << (*iter) << ": has free (1) : " << tempProc << std::endl;
				break;
			}
		}
		if(tempProc == NULL) {
			for(std::list<MemGroup *>::iterator iter = memGroupList.begin(); iter != memGroupList.end(); ++iter) {
				MemGroup * tempGroup = *iter;
				bool guard = true;
				for(std::list<MemGroup *>::iterator iterTwo = tempList.begin(); iterTwo != tempList.end(); ++iterTwo) {
					if(tempGroup == (*iterTwo)) guard = false;
				}
				if(guard && tempGroup->hasFree()) {
					tempList.push_back(tempGroup);
					tempProc = tempGroup->getFreeProc(o);
					std::cerr << tempGroup << ": has free (2) : " << tempProc << std::endl;
					break;
				}
			}
		}
	}
	return tempProc;
}

void
System::addFreeProc(ProcElement * p) 
{
	// add a processor back to the free list when it is no longer being used.
	freeProcList.push_back(p);
}

ProcElement *
System::roundRobin()
{
	ProcElement * retVal = procIter->second;
	procIter++;
	if(procIter == procMap.end()) {
		procIter = procMap.begin();
	}
	return retVal;	
}

void
System::reportChannelUsage()
{
	std::cout << "Channel Usage:" << std::endl;
	for(std::map<std::string, CommChannel *>::iterator iter = chanMap.begin();
		iter != chanMap.end(); iter++)
	{
		std::cout << "\t" << iter->first << "\t" << iter->second->getTotalCount() << std::endl;
	}
}

void
System::assignProc(ProcElement * p, Operator * o)
{
	opToProcMap[o].push_back(p);
	procToOpMap[p].push_back(o);	
}

void
System::revokeProc(ProcElement * p, Operator * o) 
{
	std::list<ProcElement *> & procList = opToProcMap[o];
	assert(!procList.empty());
	for(std::list<ProcElement*>::iterator iter = procList.begin();
		iter != procList.end(); ++iter) {
		if(*iter == p) procList.erase(iter);
		break;
	}
	std::list<Operator *> & opList = procToOpMap[p];
	for(std::list<Operator *>::iterator iter = opList.begin();
		iter != opList.end(); ++iter) {
		if(*iter == o) opList.erase(iter);
		break;
	}
}

System::SwapStruct 
System::findSwap(Operator * o)
{
	System::SwapStruct temp;
//	std::list<ProcElement *> & procList = opToProcMap[o];
	div_t result = div(procToOpMap.size(), opToProcMap.size());
	unsigned int threshold = result.quot;
//	if(procList.size() < threshold) {
	if(true) {
		//std::cerr << o->getName() << " can revoke: " << procList.size() << " < " << threshold << std::endl;
		for(std::map<Operator *, std::list<ProcElement *> >::iterator iter = opToProcMap.begin();
			iter != opToProcMap.end(); ++iter) {
		  if(iter->second.size() > threshold && iter->first != o && iter->second.size() > 1) {
				//std::cerr << iter->first->getName() << " is above threshold: " << iter->second.size() << " > " << threshold << std::endl;
				temp.foundSwap = true;
				temp.targetProc = iter->second.front();
				temp.oldOp = iter->first;
				revokeProc(temp.targetProc, temp.oldOp);
				assignProc(temp.targetProc, o);
				break;
			}
		}
	}  
	return temp;
}
}
